package com.smartinput.voice.application.service

import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.time.Instant
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.concurrent.withLock

@Service
class AliyunTokenService {

    private val logger = LoggerFactory.getLogger(AliyunTokenService::class.java)
    private val objectMapper = ObjectMapper()

    @Value("\${aliyun.access.key.id:}")
    private var accessKeyId: String = ""

    @Value("\${aliyun.access.key.secret:}")
    private var accessKeySecret: String = ""

    @Value("\${aliyun.speech.region:cn-shanghai}")
    private var region: String = "cn-shanghai"

    // Token管理
    private var aliyunToken: String? = null
    private var tokenExpireTime: Instant? = null
    private val tokenLock = ReentrantLock()

    /**
     * 获取有效的阿里云Token
     */
    fun getValidToken(): String {
        tokenLock.withLock {
            val now = Instant.now()
            // 如果token不存在或即将过期（提前5分钟刷新）
            if (aliyunToken == null || tokenExpireTime == null || now.plusSeconds(300).isAfter(tokenExpireTime)) {
                logger.info("正在获取新的阿里云Token")
                val tokenInfo = fetchAliyunToken()
                aliyunToken = tokenInfo.first
                tokenExpireTime = tokenInfo.second
                logger.info("阿里云Token获取成功，有效期至: $tokenExpireTime")
            }
            return aliyunToken!!
        }
    }

    /**
     * 强制刷新Token
     */
    fun refreshToken(): String {
        tokenLock.withLock {
            logger.info("强制刷新阿里云Token")
            val tokenInfo = fetchAliyunToken()
            aliyunToken = tokenInfo.first
            tokenExpireTime = tokenInfo.second
            logger.info("阿里云Token刷新成功，有效期至: $tokenExpireTime")
            return aliyunToken!!
        }
    }

    /**
     * 检查Token服务是否可用
     */
    fun isAvailable(): Boolean {
        return !accessKeyId.isBlank() && !accessKeySecret.isBlank()
    }

    /**
     * 调用阿里云Token接口 - 使用POP协议签名
     */
    private fun fetchAliyunToken(): Pair<String, Instant> {
        val url = "https://nls-meta.cn-shanghai.aliyuncs.com/"

        // 构建请求参数
        val parameters = mutableMapOf<String, String>()
        parameters["AccessKeyId"] = accessKeyId
        parameters["Action"] = "CreateToken"
        parameters["Format"] = "JSON"
        parameters["RegionId"] = region
        parameters["SignatureMethod"] = "HMAC-SHA1"
        parameters["SignatureNonce"] = UUID.randomUUID().toString()
        parameters["SignatureVersion"] = "1.0"
        parameters["Timestamp"] = java.time.ZonedDateTime.now(java.time.ZoneOffset.UTC)
            .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"))
        parameters["Version"] = "2019-02-28"

        // 生成签名
        val signature = generateSignature(parameters, accessKeySecret)
        parameters["Signature"] = signature

        // 构建请求URL
        val queryString = parameters.entries
            .sortedBy { it.key }
            .joinToString("&") {
                if (it.key == "Signature") {
                    "${it.key}=${it.value}"
                } else {
                    "${it.key}=${URLEncoder.encode(it.value, StandardCharsets.UTF_8)}"
                }
            }
        val fullUrl = "$url?$queryString"

        logger.debug("Token请求URL: $fullUrl")

        // 使用Kotlin标准库发送GET请求
        val client = java.net.http.HttpClient.newBuilder()
            .connectTimeout(java.time.Duration.ofSeconds(30))
            .build()

        val request = java.net.http.HttpRequest.newBuilder()
            .uri(java.net.URI.create(fullUrl))
            .header("Accept", "application/json")
            .GET()
            .build()

        val response = client.send(request, java.net.http.HttpResponse.BodyHandlers.ofString())
        val responseCode = response.statusCode()
        val responseText = response.body()

        if (responseCode != 200) {
            logger.error("获取阿里云Token失败: $responseText")
            throw RuntimeException("获取阿里云Token失败: $responseText")
        }

        val json = objectMapper.readTree(responseText)
        val token = json["Token"]["Id"].asText()
        val expireTime = Instant.ofEpochSecond(json["Token"]["ExpireTime"].asLong())

        return token to expireTime
    }

    /**
     * 生成POP协议签名
     */
    private fun generateSignature(parameters: Map<String, String>, secretKey: String): String {
        val paramsWithoutSignature = parameters.filterKeys { it != "Signature" }
        val canonicalizedQueryString = canonicalizedQuery(paramsWithoutSignature)

        val stringToSign = createStringToSign("GET", "/", canonicalizedQueryString)

        val signature = sign(stringToSign, "$secretKey&")

        return signature
    }

    private fun percentEncode(value: String?): String? {
        return if (value != null) {
            URLEncoder.encode(value, StandardCharsets.UTF_8)
                .replace("+", "%20")
                .replace("*", "%2A")
                .replace("%7E", "~")
        } else {
            null
        }
    }

    private fun canonicalizedQuery(queryParamsMap: Map<String, String>): String {
        val sortedKeys = queryParamsMap.keys.sorted()
        val canonicalizedQueryString = StringBuilder()

        for (key in sortedKeys) {
            canonicalizedQueryString.append("&")
                .append(percentEncode(key) ?: "")
                .append("=")
                .append(percentEncode(queryParamsMap[key]) ?: "")
        }

        return canonicalizedQueryString.toString().substring(1)
    }

    private fun createStringToSign(method: String, urlPath: String, queryString: String): String {
        val strBuilderSign = StringBuilder()
        strBuilderSign.append(method)
        strBuilderSign.append("&")
        strBuilderSign.append(percentEncode(urlPath) ?: "")
        strBuilderSign.append("&")
        strBuilderSign.append(percentEncode(queryString) ?: "")

        return strBuilderSign.toString()
    }

    private fun sign(stringToSign: String, accessKeySecret: String): String {
        val mac = Mac.getInstance("HmacSHA1")
        mac.init(SecretKeySpec(accessKeySecret.toByteArray(StandardCharsets.UTF_8), "HmacSHA1"))
        val signData = mac.doFinal(stringToSign.toByteArray(StandardCharsets.UTF_8))
        val signBase64 = Base64.getEncoder().encodeToString(signData)

        val signUrlEncode = percentEncode(signBase64) ?: ""

        return signUrlEncode
    }
} 